package com.sheep.secret;

import com.alibaba.fastjson.JSON;
import com.sheep.secret.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;

/**
 * 加解密切面
 *
 * @author lizy
 * @date 2021/08/04
 */
@Aspect
@Component
@Slf4j
public class SecretAOPAspect {
    @Value("${api.secret.enable:true}")
    private boolean secretEnable;

    // 定义切点,使用了@SecretAnnotation注解的类 或 使用了@SecretAnnotation注解的方法
    @Pointcut("@within(com.sheep.secret.SecretAnnotation) || @annotation(com.sheep.secret.SecretAnnotation)")
    public void pointcut(){}

    /**
     * 环绕切面
     * 这个通知定义了验签，加解密的基本逻辑
     * @param point 点
     * @return {@link Object}
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point){
        Object result = null;
        // 获取被代理方法参数
        Object[] args = point.getArgs();
        // 获取被代理对象
        Object target = point.getTarget();
        // 获取通知签名
        MethodSignature signature = (MethodSignature)point.getSignature();
 
        try {

            if (!secretEnable){
                return point.proceed(args);
            }

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            assert attributes != null;

            // request对象
            HttpServletRequest request = attributes.getRequest();
            // 获取所有的header
            HashMap<String,String> headersMap = new HashMap<>(16);
            Enumeration headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String key = (String) headerNames.nextElement();
                String value = request.getHeader(key);
                headersMap.put(key, value);
            }
            // 获取被代理方法
            Method pointMethod = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
            // 获取被代理方法上面的注解@SecretAnnotation
            SecretAnnotation secret = pointMethod.getAnnotation(SecretAnnotation.class);
            // 被代理方法上没有，则说明@SecretAnnotation注解在被代理类上
            if(secret==null){
                secret = target.getClass().getAnnotation(SecretAnnotation.class);
            }

            InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8);
            StringBuilder stringBuilder;
            try (BufferedReader br = new BufferedReader(inputStreamReader)) {
                String line;
                stringBuilder = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    stringBuilder.append(line);
                }
            }
            String body = stringBuilder.toString();

            // 获取注解上声明的加解密类
            Class<SecretService> bean = (Class<SecretService>) secret.bean();
            // 是否验签
            boolean IsVerifySign = secret.verifySign();
            // 是否解密
            boolean isDecode = secret.decode();
            // 是否加密
            boolean isEncode = secret.encode();

            SecretService signService = SpringUtil.getBean(bean);
            // 需要验签
            if (IsVerifySign) {
                Pair<Boolean, String> verifySign = signService.verifySign(headersMap, body);
                // 验签不通过
                if (!verifySign.getLeft()) {
                    return isEncode ? signService.encrypt(headersMap, verifySign.getRight()).getRight() : verifySign.getRight();
                }
            }
            // 需要解密
            if (StringUtils.isNotBlank(body) && isDecode) {
                Pair<Boolean, String> decrypt = signService.decrypt(headersMap, body);
                // 解密不通过
                if (!decrypt.getLeft()) {
                    return isEncode ? signService.encrypt(headersMap, decrypt.getRight()).getRight() : decrypt.getRight();
                }
                if (args.length>0){
                    args[0] = JSON.parseObject(decrypt.getRight(),args[0].getClass());
                }
            }
            // 执行请求（被代理的返回值必须是实际返回值的父类，或者同一个类）
            result = point.proceed(args);
            // 加密
            return isEncode ? signService.encrypt(headersMap, result).getRight() : result;
        } catch (Throwable throwable) {
            log.error("切面加密解密错误！",throwable);
        }
        return result;
    }
}
